package p2pcdnsim.cdn;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.jdom.Element;

import p2pcdnsim.exception.SurrogateAlreadyInsertedException;
import p2pcdnsim.network.Node;
import p2pcdnsim.network.RouterNode;
import p2pcdnsim.xml.XMLHandler;
import desmoj.core.simulator.SimTime;


public class DynamicReplicaPlacementNaive implements PlacementStrategy {

	private XMLHandler requests;
	private boolean sizeRequests;
	private static final int DC = 5; 
	private static final int MAXCLIENTS = 200;
	
	public DynamicReplicaPlacementNaive() {
		this.requests = null;
		this.sizeRequests = true;
	}

	public DynamicReplicaPlacementNaive(boolean sizeRequests,
			XMLHandler requests) {
		this.setSizeRequests(sizeRequests);
		this.requests = requests;
	}
	
	public static void debug(String info) {
		if (info != null) System.out.println("### DEBUG DynamicReplicaPlacementNaive ###" + info);
	}

	@Override
	public void placeSurrogates(List<Surrogate> surrogates, CDN cdn)
			throws SurrogateAlreadyInsertedException {
		int loadServer = getMAXCLIENTS();
		Map<Long, Integer> rcs = new HashMap<Long, Integer>();
		RouterNode[] nodes = cdn.getTopology().getNodes();
		for (Node node: nodes) {
			rcs.put(node.getNodeId(), getMAXCLIENTS());
		}
		Iterator<Element> i = requests.iterator();
		while (i.hasNext()) {
			Element linksTemp = i.next();
			long objectID = Long.parseLong(linksTemp
					.getChild("VideoObject").getText());
			long nodeId = cdn.getNodeOfClinet(Long.parseLong(linksTemp
					.getChild("Client").getText()));
			Double timestamp = Double.parseDouble(linksTemp
					.getChild("Timestamp").getText());
			
			
			Node client = null;
			
			Surrogate surrogateObject = null;
			
			for (Surrogate surrogate: surrogates) {
				if (((VoDSurrogate) surrogate).getCache().retrieveObject(objectID, new SimTime(timestamp)) != null) {
						surrogateObject = surrogate;
						break;
				}
				//TODO achar surrogate que tem o objeto com id ObjectID
			}
			
			if (surrogateObject == null) debug("fudeu surrogate nulo");
			else debug("acertou");
			
			for (Node node: nodes) {
				if ((node.getNodeId() == nodeId) && (node.getNetworkLayer().getTransportLayer().getApplicationLayer().getApplicationProcess() != null)) client = node;
			}
			
			if (client != null) {
				long distOvClientServer = client.getNetworkLayer().getRouterTable().getDistance(cdn.getOriginServerAddress());
				debug("Node Client with ID " + client.getNodeId() + " and DistOverlay(c,s)=" + distOvClientServer + " DC=" + getDC());
				
				boolean test1 = false,test2 = false;
				
				if(loadServer > 0){
					if(distOvClientServer <= DC){
						//TODO placement
						loadServer--;
						test1 = true;
					}
				}
				
				if (test1) continue;
				
				List<Node> intermediateNodes = client.getNetworkLayer().getRouterTable().getRoute(cdn.getOriginServerNode());
				
				Iterator<Node> iNodes = intermediateNodes.iterator();
				
				while(iNodes.hasNext()) {
					if (rcs.get(iNodes.next().getNodeId()) > 0) { 
						if (client.getNetworkLayer().getRouterTable().getDistance(iNodes.next().getNetworkLayer().getDefaultAddress()) <= getDC()) {
							//TODO placement
							if (iNodes.next() instanceof RouterNode) cdn.placeSurrogate(surrogateObject, (RouterNode) iNodes.next());
							rcs.put(iNodes.next().getNodeId(), rcs.get(iNodes.next().getNodeId()) - 1);
							debug("Node with ID " + iNodes.next().getNodeId() + " and DistOverlay(c,si)=" + client.getNetworkLayer().getRouterTable().getDistance(iNodes.next().getNetworkLayer().getDefaultAddress()) + " DC=" + getDC() + " will be surrogate of request with id=" + i.hashCode());							
							test2 = true;
							break;
						}
					}
				}
				
				if (test2) continue;
				
				
			}
			
		}

		
		// Define the vRCt -> RCt = lt - lct

		// Define the loads
		/*Map<Long, Long> loads = new HashMap<Long, Long>();
		{
			for (Node node : nodes)
				loads.put(node.getNodeId(), 0L);
			Iterator<Element> i = requests.iterator();
			while (i.hasNext()) {
				Element linksTemp = i.next();
				@SuppressWarnings("unused")
				long objectID = Long.parseLong(linksTemp
						.getChild("VideoObject").getText());
				long nodeId = cdn.getNodeOfClinet(Long.parseLong(linksTemp
						.getChild("Client").getText()));
				long load = loads.get(nodeId);
				// load += sizeRequests ? cdn.getOriginServer().contents.get(
				// objectID).getLength() : 1;
				loads.put(nodeId, load);
				// System.out.println("Load on router "+nodeId+": "+load);
			}
		}

		// Place the surrogates
		for (Surrogate surrogate : surrogates) {
			long bestPlaceCost = Long.MAX_VALUE;
			RouterNode chosenNode = null;
			// for each potential site
			for (int potentialSite = 0; potentialSite < nodes.length; potentialSite++) {
				long placeCost;
				// System.out.println("Evaluating placement on node "+nodes[place].getNodeId());
				if (nodes[potentialSite].getNetworkLayer().getTransportLayer()
						.getApplicationLayer().getApplicationProcess() != null) {
					placeCost = Long.MAX_VALUE;
				} else {
					placeCost = 0;
					for (Node node : nodes) {
						// System.out.println("Evaluating cost node "+node.getNodeId());
						long load = loads.get(node.getNodeId());
						// System.out.println("Load on router "+node.getNodeId()+": "+load);
						long costToOriginServer = load
								* (1 + node.getNetworkLayer().getRouterTable()
										.getDistance(
												cdn.getOriginServerAddress()));
						// System.out.print(costToOriginServer+" ");
						long costToClosestSurrogate = Long.MAX_VALUE;
						Iterator<Surrogate> it = cdn.getSurrogates().iterator();
						while (it.hasNext()) {
							long cost = load
									* (1 + node
											.getNetworkLayer()
											.getRouterTable()
											.getDistance(
													cdn
															.getSurrogateNode(
																	it.next())
															.getNetworkLayer()
															.getDefaultAddress()));
							if (cost < costToClosestSurrogate)
								costToClosestSurrogate = cost;
						}
						// System.out.print(costToClosestSurrogate+" ");
						long costOnPlacement = load
								* (1 + node.getNetworkLayer().getRouterTable()
										.getDistance(
												nodes[potentialSite]
														.getNetworkLayer()
														.getDefaultAddress()));
						// System.out.print(costOnPlacement+" ");
						placeCost += Math.min(costOnPlacement, Math.min(
								costToOriginServer, costToClosestSurrogate));
						// System.out.println(totalCost[place]+" ");
					}
				}
				// System.out.println("Placement cost on node "+nodes[place].getNodeId()+": "
				// +placeCost);
				if (placeCost < bestPlaceCost) {
					bestPlaceCost = placeCost;
					chosenNode = nodes[potentialSite];
				}
			}
			// System.out.println(chosenNode.getNodeId());
			cdn.placeSurrogate(surrogate, chosenNode);
		}*/
	}
	

	public void setSizeRequests(boolean sizeRequests) {
		this.sizeRequests = sizeRequests;
	}

	public boolean isSizeRequests() {
		return sizeRequests;
	}

	public static int getDC() {
		return DC;
	}

	public static int getMAXCLIENTS() {
		return MAXCLIENTS;
	}

}
